#include "crossbit/common.h"
#include "crossbit/Interpreter.h"
#include "crossbit/MemoryImage.h"
#include "crossbit/ELFMemoryImage.h"

using namespace crossbit;


// Implementation related header info
#include "arch/mips/MIPSSyscallHandler.h"
#include "MIPSSyscallInternal.h"

MIPSSyscallHandler::MIPSSyscallHandler(ProcessorState *state, MemoryImage *img)
: SyscallHandler(state, img)
{
}

MIPSSyscallHandler::~MIPSSyscallHandler()
{
}

XTInt32 MIPSSyscallHandler::syscode()
{
								return (XTInt32)(*(XTInt32*)sh_state->get(2));
}

// Emulate SimpleScalar SStrix syscall
void MIPSSyscallHandler::operator () ()
{
								/* address of the first element of register file */
								XTInt32 *r = (XTInt32 *)sh_state->get(0); 
								XTInt32 syscode = (XTInt32)r[2];

								switch(syscode) {
																case MIPS_SYS_read:
																								{
																																r[2] = SYSCALL::XTRead((XTInt32)r[4], /*fd*/
																																																(XTInt8 *)(XTMemAddr)r[5], /*buf*/
																																																(XTInt32)r[6] /*nbyte*/);
																																if ((XTInt32)r[2] != -1)
																																								r[7] = 0;
																																else {
																																								r[7] = 1;
																																}

																																break;
																								}

																case MIPS_SYS_write:
																								{
																																r[2] = SYSCALL::XTWrite((XTInt32)r[4], /*fd*/
																																																(XTInt8 *)((XTMemAddr)r[5]), /*buf*/
																																																(XTInt32)r[6] /*nbyte*/);
																																if (r[2] == r[6])
																																								r[7] = 0;
																																else {
																																								r[7] = 1;
																																}	

																																break;
																								}
																case MIPS_SYS_open:
																								{
																																XTInt32 mips_flags = (XTInt32)r[5],
																																								local_flags = 0;

																																/* translate open(2) flags */
																																for (XTUint32 i = 0; i < MIPS_NFLAGS; i++) {
																																								if (mips_flags & mips_flag_table[i].mips_flag)
																																																local_flags |= mips_flag_table[i].local_flag;
																																}

																																/* open the file */
																																/*fd*/ r[2] = SYSCALL::XTOpen((const XTPStr)r[4], /*fname*/
																																																local_flags,
																																																(mode_t)r[6] /*mode*/);

																																/* check for an error condition */
																																if ((XTInt32)r[2] != -1)
																																								r[7] = 0;
																																else {
																																								r[7] = 1;
																																}
																																break;
																								}
																case MIPS_SYS_close:
																								{
																																XTInt32 fd = r[4];
																																// don't close stdin, stdout, or stderr
																																if (fd == 0 || fd == 1 || fd == 2)
																																{
																																								r[7] = 0;
																																								break;
																																}

																																// close the file
																																r[2] = SYSCALL::XTClose(fd);

																																// check for an error condition
																																if ((XTInt32)r[2] != -1)
																																								r[7] = 0;
																																else {
																																								r[7] = 1;
																																}
																																break;
																								}


																case MIPS_SYS_getuid:
																								{
																																// get user id
																																r[2] = SYSCALL::XTGetuid();

																																//check for an erro conditon
																																if ((XTInt32)r[2] != -1)
																																								r[7] = 0;
																																else {
																																								r[7] = 1;
																																}
																																break;
																								}
																case MIPS_SYS_brk:
																								{
																																r[2] = (XTInt32)SYSCALL::XTBrk((void*)r[4]);	//added by zhengjuyu

																																if ((signed)r[2] != -1 )
																																{
																																								r[7] = 0;
																																}
																																else {
																																								r[2] = -1;   // WARN: errno might be inaccurate, dependent on 
																																								// MemorImage::brk's implementation
																																								r[7] = 1;
																																}
																																break;
																								}

																case MIPS_SYS_getgid:
																								{
																																r[2] = SYSCALL::XTGetgid();

																																if ((XTInt32)r[2] != -1)
																																								r[7] = 0;
																																else {
																																								r[7] = 1;
																																}
																																break;
																								}

																case MIPS_SYS_geteuid:
																								{
																																// get effective user id
																																r[2] = SYSCALL::XTGeteuid();

																																//check for an erro conditon
																																if ((XTInt32)r[2] != -1)
																																								r[7] = 0;
																																else {
																																								r[7] = 1;
																																}
																																break;
																								}

																case MIPS_SYS_getegid:
																								{
																																r[2] = SYSCALL::XTGetegid();

																																if ((XTInt32)r[2] != -1)
																																								r[7] = 0;
																																else {
																																								r[7] = 1;
																																}
																																break;
																								}
																case MIPS_SYS_ioctl:
																								{
																																XTInt8 buf[NUM_IOCTL_BYTES];

																																/* if arg ptr exists, copy NUM_IOCTL_BYTES bytes to host mem */
																																if (r[6] != 0)
																																								UTIL::XTMemcpy((void*)buf, (void *)(XTMemAddr)r[6], NUM_IOCTL_BYTES);

																																/* perform the ioctl() call */
																																//XXX r[2] = SYSCALL::XTIoctl(/*fd*/r[4], r[5], buf);
																																r[2] = ioctl(/*fd*/r[4], r[5], buf);

																																/* if arg ptr exists, copy NUM_IOCTL_BYTES bytes from host mem */
																																if (r[6] != 0)
																																								UTIL::XTMemcpy((void *)(XTMemAddr)r[6], buf, NUM_IOCTL_BYTES);

																																/* check for an error condition */
																																if ((XTInt32)r[2] != -1)
																																								r[7] = 0;
																																else
																																{	
																																								/* got an error, return details */
																																								r[2] = errno;
																																								r[7] = 1;
																																}
																								}
																								break;

																case MIPS_SYS_setrlimit:
																								{
																																struct rlimit *rl;

																																rl = (struct rlimit*)(XTMemAddr)r[5];

																																r[2] = SYSCALL::XTSetrlimit( (XTInt32)r[4], rl);

																																if ( (XTInt32)r[2] != -1)
																																								r[7] = 0;
																																else {
																																								r[2] = errno;
																																								r[7] = 0;
																																}

																																break;

																								}

																case MIPS_SYS_getrlimit:
																								{
																																struct rlimit *rl;	

																																rl = (struct rlimit *)(XTMemAddr)r[5];

																																r[2] = SYSCALL::XTGetrlimit( (XTInt32)r[4], // resource
																																																rl);	 // rlim

																																if ( (XTInt32)r[2] != -1)
																																								r[7] = 0;
																																else {
																																								r[2] = errno;
																																								r[7] = 0;
																																}

																																break;
																								}

																case MIPS_SYS_getrusage:
																								{
																																struct rusage local_rusage;

																																r[2] = SYSCALL::XTGetrusage( (XTInt32)r[4], &local_rusage);

																																if( r[2] != -1 )
																																								r[7] = 0;
																																else
																																{
																																								r[2] = errno;
																																								r[7] = 1;
																																}

																																UTIL::XTMemcpy((void*)r[5], &local_rusage, sizeof(struct rusage));

																																break;

																								}

																case MIPS_SYS_mmap:
																								{
																																XTInt32 fd = *(XTInt32*)(r[29]+16);
																																XTInt32 offset = *(XTInt32*)(r[29] + 20);

																																r[2] = (XTInt32)SYSCALL::XTMmap( (void*)r[4], r[5], r[6], r[7], fd, offset);

																																if( (void*)r[2] != MAP_FAILED)
																																								r[7] = 0;
																																else
																																								r[7] = 1;
																																break;
																								}
																case MIPS_SYS_munmap:
																								{
																																//dummy
																																r[2] = SYSCALL::XTMunmap((void*)r[4], r[5]);
																																r[2] = r[7] = 0;
																																break;
																								}

																case MIPS_SYS_uname:
																								{
																																struct utsname* buf = (struct utsname*)(r[4]);
																																r[2] = SYSCALL::XTUname( buf );
																																r[7] = 0;
																																break;
																								}

															case MIPS_SYS_unlink:
																								{
																																r[2] = SYSCALL::XTUnlink((XTPStr)r[4]); // filename

																																// check for an error condition
																																if ((int)r[2] != -1)
																																								r[7] = 0;
																																else {
																																								r[2] = errno;
																																								r[7] = 1;
																																}
																																break;
																								}
																case MIPS_SYS_readlink:
																								{
																																XTPStr path = (XTPStr)r[4];	
																																XTPStr buf = (XTPStr)r[5];
																																size_t bufsiz = (size_t)r[6];

																																r[2] = SYSCALL::XTReadlink(path, buf, bufsiz);

																																if((XTInt32)r[2] != -1)
																																{
																																								r[7] = 0;
																																}
																																else 
																																{
																																								r[2] = errno;
																																								r[7] = 1;
																																}
																																break;
																								}
																case MIPS_SYS_times:
																								{
																																struct tms* buf = (struct tms*)r[4];
																																r[2] = SYSCALL::XTTimes(buf);

																																if((XTInt32)r[2] != -1)
																																								r[7] = 0;
																																else
																																{
																																								r[2] = errno;
																																								r[7] = 1;
																																}
																																break;
																								}
																case MIPS_SYS_time:
																								{
																																time_t * t = (time_t*)r[4];

																																r[2] = SYSCALL::XTTime(t);

																																if((int)r[2] != -1)
																																								r[7] = 0;
																																else
																																{
																																								r[2] = errno;
																																								r[7] = 1;
																																}
																																break;
																								}
																case MIPS_SYS_stat:
																								{
																																XTPStr file_name = (XTPStr)r[4];
																																struct stat * buf = (struct stat*)r[5];
																																r[2] = SYSCALL::XTStat(file_name, buf);

																																if((int)r[2] != -1)
																																								r[7] = 0;
																																else
																																{
																																								r[2] = errno;
																																								r[7] = 1;
																																}
																																break;
																								}
																case MIPS_SYS_fstat:
																								{
																																struct stat * buf = (struct stat*)r[5];
																																r[2] = SYSCALL::XTFstat(r[4], buf);

																																if ((int)r[2] != -1)
																																								r[7] = 0;
																																else
																																{
																																								r[2] = errno;
																																								r[7] = 1;
																																}
																																break;
																								}
																case MIPS_SYS_getpid:
																								{
																																r[2] = SYSCALL::XTGetpid();

																																if ((XTInt32)r[2] != -1)
																																								r[7] = 0;
																																else {
																																								r[2] = errno;
																																								r[7] = 1;
																																}
																																break;
																								}
																case MIPS_SYS_rename:
																								{
																																r[2] = SYSCALL::XTRename((XTPStr)r[4], (XTPStr)r[5]);

																																if((XTInt32)r[2] != -1)
																																								r[7] = 0;
																																else {
																																								r[2] = errno;
																																								r[7] = 1;
																																}
																																break;
																								}
																case MIPS_SYS_lseek:
																								{
																																r[2] = lseek((int)r[4], (long)r[5], (int)r[6]);

																																if((int)r[2] != -1)
																																								r[7] = 0;
																																else {
																																								r[2] = errno;
																																								r[7] = 1;
																																}
																																break;
																								}
																case MIPS_SYS_rt_sigaction:
																								{
																																//dummy
																																r[2] = 0;
																																r[7] = 0;

																																break;
																								}

																case MIPS_SYS_fstat64:
																								{
																																struct stat64 sbuf;

																																r[2] = fstat64((XTInt32)r[4], &sbuf);

																																// check for error conditon
																																if ((XTInt32)r[2] != -1)
																																								r[7] = 0;
																																else {
																																								r[2] = errno;
																																								r[7] = 1;
																																}

																																*(struct stat64*)((XTMemAddr)r[5]) = sbuf;

																																break;
																								}

																case MIPS_SYS_fcntl64:
																								{
																																r[2] = SYSCALL::XTFcntl( (XTInt32)r[4], /*fd*/
																																																(XTInt32)r[5], /*cmd*/
																																																(XTInt64)r[6] /*arg*/);

																																// check for an error condition
																																if ( (XTInt32)r[2] != -1)
																																								r[7] = 0;
																																else {
																																								r[2] = errno;
																																								r[7] = 1;
																																}
																																break;
																								}
	
																case MIPS_SYS_exitgroup:
																								{
																																SYSCALL::XTExit(2);
																																break;
																								}

																default:
																								UTIL::XTLOG("Unknown syscall: 0x%x\n", syscode);
																								assert(0 && "Unrecognized syscall\n");
								}
}
